import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model, Types } from 'mongoose';
import { Website, WebsiteDocument } from './schemas/website.schema';
import { PageView, PageViewDocument } from './schemas/page-view.schema';
import { WebsiteQueryDto, TimeRange } from './dto/website-query.dto';

@Injectable()
export class StatsMongoService {
  constructor(
    @InjectModel(Website.name)
    private websiteModel: Model<WebsiteDocument>,
    @InjectModel(PageView.name)
    private pageViewModel: Model<PageViewDocument>,
  ) {}

  private async getWebsiteById(id: string): Promise<WebsiteDocument> {
    const website = await this.websiteModel.findById(id);
    if (!website) throw new Error('网站不存在');
    return website;
  }

  private getDateRange(timeRange: TimeRange, startDate?: string, endDate?: string): { startDate: Date, endDate: Date } {
    const now = new Date();
    let start: Date;
    let end: Date = now;
    
    if (startDate && endDate) {
      start = new Date(startDate);
      end = new Date(endDate);
    } else {
      switch (timeRange) {
        case TimeRange.TODAY:
          start = new Date(now.getFullYear(), now.getMonth(), now.getDate());
          break;
        case TimeRange.YESTERDAY:
          start = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
          end = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1, 23, 59, 59, 999);
          break;
        case TimeRange.LAST_7_DAYS:
          start = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
          break;
        case TimeRange.LAST_30_DAYS:
          start = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
          break;
        case TimeRange.THIS_MONTH:
          start = new Date(now.getFullYear(), now.getMonth(), 1);
          break;
        case TimeRange.LAST_MONTH:
          start = new Date(now.getFullYear(), now.getMonth() - 1, 1);
          end = new Date(now.getFullYear(), now.getMonth(), 0, 23, 59, 59, 999);
          break;
        default:
          start = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
      }
    }
    return { startDate: start, endDate: end };
  }

  private buildMatchStage(websiteId: string, startDate: Date, endDate: Date, query: WebsiteQueryDto): any {
    const match: any = {
      websiteId: new Types.ObjectId(websiteId),
      createdAt: { $gte: startDate, $lte: endDate }
    };

    if (query.url) match.url = { $regex: query.url, $options: 'i' };
    if (query.referrer) match.referrer = { $regex: query.referrer, $options: 'i' };
    if (query.country) match.country = query.country;
    if (query.browser) match.browser = query.browser;
    if (query.os) match.os = query.os;
    if (query.device) match.device = query.device;

    return match;
  }

  private async getPageViewsByDay(websiteId: string, startDate: Date, endDate: Date, query: WebsiteQueryDto): Promise<any[]> {
    const match = this.buildMatchStage(websiteId, startDate, endDate, query);
    
    return this.pageViewModel.aggregate([
      { $match: match },
      {
        $group: {
          _id: {
            $dateToString: { format: "%Y-%m-%d", date: "$createdAt" }
          },
          count: { $sum: 1 },
          uniqueCount: { $addToSet: "$sessionId" }
        }
      },
      {
        $project: {
          date: "$_id",
          count: 1,
          uniqueCount: { $size: "$uniqueCount" }
        }
      },
      { $sort: { date: 1 } }
    ]);
  }

  private async getPageViewsByWeek(websiteId: string, startDate: Date, endDate: Date, query: WebsiteQueryDto): Promise<any[]> {
    const match = this.buildMatchStage(websiteId, startDate, endDate, query);
    
    return this.pageViewModel.aggregate([
      { $match: match },
      {
        $group: {
          _id: {
            $dateToString: { format: "%Y-W%U", date: "$createdAt" }
          },
          count: { $sum: 1 },
          uniqueCount: { $addToSet: "$sessionId" }
        }
      },
      {
        $project: {
          week: "$_id",
          count: 1,
          uniqueCount: { $size: "$uniqueCount" }
        }
      },
      { $sort: { week: 1 } }
    ]);
  }

  private async getPageViewsByMonth(websiteId: string, startDate: Date, endDate: Date, query: WebsiteQueryDto): Promise<any[]> {
    const match = this.buildMatchStage(websiteId, startDate, endDate, query);
    
    return this.pageViewModel.aggregate([
      { $match: match },
      {
        $group: {
          _id: {
            $dateToString: { format: "%Y-%m", date: "$createdAt" }
          },
          count: { $sum: 1 },
          uniqueCount: { $addToSet: "$sessionId" }
        }
      },
      {
        $project: {
          month: "$_id",
          count: 1,
          uniqueCount: { $size: "$uniqueCount" }
        }
      },
      { $sort: { month: 1 } }
    ]);
  }

  private async getPageViewsByField(websiteId: string, startDate: Date, endDate: Date, query: WebsiteQueryDto, field: string, limit: number = 10, offset: number = 0): Promise<any[]> {
    const match = this.buildMatchStage(websiteId, startDate, endDate, query);
    
    return this.pageViewModel.aggregate([
      { $match: match },
      {
        $group: {
          _id: `$${field}`,
          count: { $sum: 1 },
          uniqueCount: { $addToSet: "$sessionId" }
        }
      },
      {
        $project: {
          [field]: "$_id",
          count: 1,
          uniqueCount: { $size: "$uniqueCount" }
        }
      },
      { $sort: { count: -1 } },
      { $skip: offset },
      { $limit: limit }
    ]);
  }

  async getWebsiteStats(id: string, query: WebsiteQueryDto): Promise<any> {
    const website = await this.getWebsiteById(id);
    const { startDate, endDate } = this.getDateRange(query.timeRange, query.startDate, query.endDate);
    const match = this.buildMatchStage(id, startDate, endDate, query);

    // 获取基础统计数据
    const [basicStats] = await this.pageViewModel.aggregate([
      { $match: match },
      {
        $group: {
          _id: null,
          totalPageViews: { $sum: 1 },
          uniqueSessions: { $addToSet: "$sessionId" },
          allSessions: { $push: "$sessionId" }
        }
      },
      {
        $project: {
          totalPageViews: 1,
          uniquePageViews: { $size: "$uniqueSessions" },
          totalSessions: { $size: "$uniqueSessions" },
          allSessions: 1
        }
      }
    ]);

    // 计算跳出率（单页面会话数）
    const bounceSessionsResult = await this.pageViewModel.aggregate([
      { $match: match },
      {
        $group: {
          _id: "$sessionId",
          count: { $sum: 1 }
        }
      },
      {
        $match: { count: 1 }
      },
      {
        $group: {
          _id: null,
          bounceSessions: { $sum: 1 }
        }
      }
    ]);

    const bounceSessions = bounceSessionsResult[0]?.bounceSessions || 0;
    const totalSessions = basicStats?.totalSessions || 0;
    const bounceRate = totalSessions > 0 ? Math.round((bounceSessions / totalSessions) * 1000) / 10 : 0;

    // 获取各种维度的数据
    const [
      dataByDay,
      dataByWeek,
      dataByMonth,
      topUrls,
      topReferrers,
      topBrowsers,
      topOS,
      topDevices,
      topCountries
    ] = await Promise.all([
      this.getPageViewsByDay(id, startDate, endDate, query),
      this.getPageViewsByWeek(id, startDate, endDate, query),
      this.getPageViewsByMonth(id, startDate, endDate, query),
      this.getPageViewsByField(id, startDate, endDate, query, 'url', 10, 0),
      this.getPageViewsByField(id, startDate, endDate, query, 'referrer', 10, 0),
      this.getPageViewsByField(id, startDate, endDate, query, 'browser', 10, 0),
      this.getPageViewsByField(id, startDate, endDate, query, 'os', 10, 0),
      this.getPageViewsByField(id, startDate, endDate, query, 'device', 10, 0),
      this.getPageViewsByField(id, startDate, endDate, query, 'country', 10, 0)
    ]);

    const normalize = (arr, key) => (arr || []).map(item => ({
      [key]: item[key] ?? item[Object.keys(item)[0]],
      count: Number(item.count) || 0
    }));

    return {
      summary: {
        totalPageViews: Number(basicStats?.totalPageViews) || 0,
        uniquePageViews: Number(basicStats?.uniquePageViews) || 0,
        bounceRate: Number(bounceRate)
      },
      data: (query.groupBy === 'week' ? dataByWeek : query.groupBy === 'month' ? dataByMonth : dataByDay).map(d => ({
        date: d.date || d.week || d.month,
        pv: Number(d.count) || 0,
        uv: Number(d.uniqueCount) || 0,
      })),
      referrers: normalize(topReferrers, 'referrer'),
      devices: normalize(topDevices, 'device'),
      browsers: normalize(topBrowsers, 'browser'),
      os: normalize(topOS, 'os'),
      countries: normalize(topCountries, 'country'),
      urls: normalize(topUrls, 'url')
    };
  }

  async getWebsiteSummary(id: string, query: WebsiteQueryDto): Promise<any> {
    const website = await this.getWebsiteById(id);
    const { startDate, endDate } = this.getDateRange(query.timeRange, query.startDate, query.endDate);
    const match = this.buildMatchStage(id, startDate, endDate, query);

    const [basicStats] = await this.pageViewModel.aggregate([
      { $match: match },
      {
        $group: {
          _id: null,
          totalPageViews: { $sum: 1 },
          uniqueSessions: { $addToSet: "$sessionId" }
        }
      },
      {
        $project: {
          totalPageViews: 1,
          uniquePageViews: { $size: "$uniqueSessions" },
          totalSessions: { $size: "$uniqueSessions" }
        }
      }
    ]);

    const bounceSessionsResult = await this.pageViewModel.aggregate([
      { $match: match },
      {
        $group: {
          _id: "$sessionId",
          count: { $sum: 1 }
        }
      },
      {
        $match: { count: 1 }
      },
      {
        $group: {
          _id: null,
          bounceSessions: { $sum: 1 }
        }
      }
    ]);

    const bounceSessions = bounceSessionsResult[0]?.bounceSessions || 0;
    const totalSessions = basicStats?.totalSessions || 0;
    const totalPageViews = basicStats?.totalPageViews || 0;
    const uniquePageViews = basicStats?.uniquePageViews || 0;
    
    const bounceRate = totalSessions > 0 ? Math.round((bounceSessions / totalSessions) * 1000) / 10 : 0;
    const avgPageViews = uniquePageViews > 0 ? Math.round((totalPageViews / uniquePageViews) * 10) / 10 : 0;

    return {
      totalPageViews: Number(totalPageViews) || 0,
      uniquePageViews: Number(uniquePageViews) || 0,
      avgPageViews,
      bounceRate: Number(bounceRate)
    };
  }

  async getWebsiteTrend(id: string, query: WebsiteQueryDto): Promise<any[]> {
    const website = await this.getWebsiteById(id);
    const { startDate, endDate } = this.getDateRange(query.timeRange, query.startDate, query.endDate);
    const groupBy = query.groupBy || 'day';

    let trendData: any[] = [];
    if (groupBy === 'week') {
      trendData = await this.getPageViewsByWeek(id, startDate, endDate, query);
    } else if (groupBy === 'month') {
      trendData = await this.getPageViewsByMonth(id, startDate, endDate, query);
    } else {
      trendData = await this.getPageViewsByDay(id, startDate, endDate, query);
    }

    return trendData.map(d => ({
      date: d.date || d.week || d.month,
      pv: Number(d.count) || 0,
      uv: Number(d.uniqueCount) || 0,
    }));
  }
}
